package com.sunline.gateway.filters;


import com.sunline.gateway.exception.ErrorInfo;
import com.sunline.gateway.utils.AESUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;

@Component
public class DecryptRequestBodyGatewayFilterFactory extends AbstractGatewayFilterFactory implements Ordered {

    static Logger log = LoggerFactory.getLogger(DecryptRequestBodyGatewayFilterFactory.class);
    private final List<HttpMessageReader<?>> messageReaders;

    @Value("${AES.key}")
    private String key;

    public DecryptRequestBodyGatewayFilterFactory() {
        this.messageReaders = HandlerStrategies.withDefaults().messageReaders();
    }

    @Override
    @SuppressWarnings("unchecked")
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerRequest serverRequest = ServerRequest.create(exchange,
                    this.messageReaders);
            ServerHttpRequest request = exchange.getRequest();
            log.info("请求路径：{}", request.getPath());
            String encrypt;
            try {
                encrypt = request.getHeaders().get("encrypt").get(0);
            } catch (NullPointerException e) {
                encrypt = "true";
            }
            //请求头设置encrypt为false
            if ("false".equals(encrypt)) {
                log.info("该请求设置encrypt为false，不解密");
                return chain.filter(exchange);
            }
            // 判定是否为post请求且为JSON格式
            MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
            HttpHeaders headers = new HttpHeaders();
            headers.putAll(exchange.getRequest().getHeaders());
            headers.remove(HttpHeaders.CONTENT_LENGTH);
            CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
            if (mediaType != null
                    && mediaType.includes(MediaType.APPLICATION_JSON)
                    && "POST".equals(Objects.requireNonNull(request.getMethod()).name())) {
                log.info("请求解密");
                Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
                        .flatMap(originalBody -> modifyBody().apply(exchange, Mono.just(originalBody)));
                BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
                return bodyInserter.insert(outputMessage, new BodyInserterContext())
                        .then(Mono.defer(() -> {
                            ServerHttpRequest decorator = decorate(exchange, headers, outputMessage);
                            return chain.filter(exchange.mutate().request(decorator).build());
                        }));
            }
            return chain.filter(exchange);
        };
    }

    private BiFunction<ServerWebExchange, Mono<String>, Mono<String>> modifyBody() {

        return (exchange, body) -> {
            AtomicReference<String> result = new AtomicReference<>();
            body.subscribe(value -> {
                        try {
                            //IOS特殊处理
                            value=value.replace("\\r","").replace("\\n","").replace("\\","").replace("\"","");
                            result.set(AESUtil.decryptAES(value,key));

                        } catch (Exception e) {
                            log.error("当前错误码为[{}],当前错误描述为:[{}]",ErrorInfo.REQUEST_INFO_ERROR.getCode(),ErrorInfo.REQUEST_INFO_ERROR.getMsg());
                            log.error("当前报错信息为:[{}]",e.getMessage());
                        }
                    }
            );
            if (result.get()==null){
                return Mono.error(new RuntimeException(ErrorInfo.REQUEST_INFO_ERROR.getCode()+","+ErrorInfo.REQUEST_INFO_ERROR.getMsg()));
            }
            return Mono.just(result.get());
        };
    }

    private ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers,
                                                CachedBodyOutputMessage outputMessage) {
        return new ServerHttpRequestDecorator(exchange.getRequest()) {
            @Override
            public HttpHeaders getHeaders() {
                long contentLength = headers.getContentLength();
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                if (contentLength > 0) {
                    httpHeaders.setContentLength(contentLength);
                }
                else {
                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                }
                return httpHeaders;
            }

            @Override
            public Flux<DataBuffer> getBody() {
                return outputMessage.getBody();
            }
        };

    }

    @Override
    public int getOrder() {
        return -2;
    }

}
